package syncer

import (
	"encoding/json"
	"errors"
	"fmt"
	"gluenet/pkg/apis"
	"gluenet/pkg/gerrors"
	"gluenet/pkg/meta"
	"gluenet/pkg/platform/glue"
	"gopkg.in/yaml.v3"
	"strings"
	"time"
)

func syncTree(c *apis.StorageContext) {
	var (
		err  error
		zone *meta.Zone
	)

	t := time.Now()

	zone = &meta.Zone{
		Name:       "glue",
		Type:       "zone",
		Guid:       "0",
		UpdateTime: t,
		State:      "alive",
		Info:       nil,
	}

	areas, err := area(c, t)
	if err != nil {
		c.Logger.Errorf("deal areas %v", err)
		return
	}

	zone.ChildSize = len(areas)
	zone.ChildAlive = len(areas)
	zone.ChildData = areas

	bts, _ := json.Marshal(zone)

	c.DataBase.MapPut(&meta.Tree{
		UpdateTime:   fmt.Sprintf("%s", t.Unix()),
		ResourceTree: string(bts),
	})
}

func area(c *apis.StorageContext, t time.Time) ([]interface{}, error) {
	var agent []meta.Agent
	if err := c.DataBase.AgentListAll(&agent); err != nil {
		return nil, fmt.Errorf("query agents %v", err)
	}

	var areas = make(map[string]int)
	for _, ags := range agent {
		areas[ags.Area]++
	}

	//c.Logger.Debugf("areas %v", areas)

	var zones []interface{}
	for k, v := range areas {
		var z = meta.Zone{
			Name:       k,
			Type:       "zone",
			Guid:       "0",
			UpdateTime: t,
			State:      "alive",
			Info:       nil,
			ChildSize:  v,
			ChildAlive: v,
		}

		childs, err := agents(c, t, k, agent)
		if err != nil {
			return nil, fmt.Errorf("get childe agents %v", err)
		}
		z.ChildData = childs
		z.ChildSize = len(childs)
		z.ChildAlive = len(childs)

		zones = append(zones, z)
	}
	return zones, nil
}

func agents(c *apis.StorageContext, t time.Time, area string, agents []meta.Agent) ([]interface{}, error) {
	var data []interface{}
	for _, v := range agents {
		if v.Area == area {

			tags, err := meta.DeCodeTags(v.Tags)
			if err != nil {
				return nil, fmt.Errorf("decode tags %v", err)
			}

			var z = meta.Zone{
				Name:       v.Hostname,
				Type:       "machine",
				Guid:       v.Guid,
				UpdateTime: t,
				State:      "alive",
				Info:       v,
			}

			var res []interface{}
			switch tags["platform"] {
			case apis.PlatfromKubernetes:
				res, err = platformk8s(c, t, &v)
				z.Type = "zone"
			case apis.PlatfromGluenets:
				z.Type = "machine"
				res, err = platformGluenets(c, t, &v)
			default:
				err = fmt.Errorf("unkwon agent type [%v]", tags["type"])
			}
			if err != nil {
				return nil, fmt.Errorf("get platfrom apps %v", err)
			}
			z.ChildData = res
			z.ChildSize = len(res)
			z.ChildAlive = len(res)

			data = append(data, z)
		}
	}

	return data, nil
}

func platformk8s(c *apis.StorageContext, t time.Time, agent *meta.Agent) ([]interface{}, error) {
	var aruntime *meta.AgentRunTime
	if err := c.DataBase.AgentRuntimeGet(agent.Guid, &aruntime); err != nil {
		return nil, fmt.Errorf("k8s agent runtime %v", err)
	}
	hosts := strings.Split(aruntime.Hosts, "|")

	//var infos *meta.Info
	//if err := c.DataBase.InfosQuery(ops.K8sNodeInfo, agent.Guid, &infos); err != nil {
	//	return nil, err
	//}
	//var nodes = infos.Infos.([]map[string]interface{})

	//c.Logger.Errorf("nodes %v", nodes)

	var workers []interface{}
	for _, v := range hosts {
		var z = meta.Zone{
			Name:       v,
			Type:       "machine",
			Guid:       agent.Guid,
			UpdateTime: t,
			State:      "alive",
			Info:       nil,
		}

		apps, err := k8sNodeApps(c, t, z.Name, agent.Guid)
		if err != nil {
			return nil, fmt.Errorf("k8s node apps %v", err)
		}
		z.ChildData = apps
		z.ChildSize = len(apps)
		z.ChildAlive = len(apps)
		workers = append(workers, z)
	}

	return workers, nil
}

func k8sNodeApps(c *apis.StorageContext, t time.Time, node, guid string) ([]interface{}, error) {
	var agentRuntime *meta.AgentRunTime
	if err := c.DataBase.AgentRuntimeGet(guid, &agentRuntime); err != nil {
		if errors.Is(err, gerrors.DataNotFind) {
			return nil, nil
		}
		return nil, err
	}
	acts := strings.Split(agentRuntime.Containers, "|")
	var apps = make(map[string][]string)
	for _, v := range acts {
		infos := strings.Split(v, ",")
		if len(infos) == 4 {
			appGuid, appContainer, _, nodeName := infos[0], infos[1], infos[2], infos[3]
			if nodeName == node {
				apps[appGuid] = append(apps[appGuid], appContainer)
			}
		}
	}

	//c.Logger.Debugf("k8s node apps: %v", apps)

	var appsData []interface{}

	for k, v := range apps {
		var ins *meta.Instance
		if err := c.DataBase.InstanceGet(k, &ins); err != nil {
			if errors.Is(err, gerrors.DataNotFind) {
				continue
			}
			return nil, fmt.Errorf("get agent error %v", err)
		}

		var z = meta.Zone{
			Name:       ins.Name,
			Type:       "instance",
			Guid:       k,
			UpdateTime: t,
			State:      "alive",
			Info:       ins,
		}

		z.ChildAlive = len(v)
		z.ChildSize = len(v)

		containers, err := k8sContainerInfo(c, t, guid, k, v)
		if err != nil {
			return nil, err
		}
		z.ChildData = containers

		appsData = append(appsData, z)
	}
	return appsData, nil
}

func k8sContainerInfo(c *apis.StorageContext, t time.Time, agentGuid string, instanceGuid string, containers []string) ([]interface{}, error) {
	var irun *meta.InstanceRunTime
	if err := c.DataBase.InstanceRuntimeGet(instanceGuid, &irun); err != nil {
		return nil, fmt.Errorf("can't find instance runtime %v", err)
	}
	if irun.ContainersInfo == "" {
		return nil, nil
	}

	var infos map[string]interface{}
	if err := json.Unmarshal([]byte(irun.ContainersInfo), &infos); err != nil {
		return nil, fmt.Errorf("unmarshall contaienr info %v", err)
	}

	var res []interface{}
	for _, v := range containers {
		var z = meta.Zone{
			Name:       v,
			Type:       "container",
			Guid:       instanceGuid,
			UpdateTime: t,
			State:      "alive",
			Info:       infos[v],
		}

		z.ChildAlive = 0
		z.ChildData = nil
		z.ChildSize = 0

		res = append(res, z)
	}

	return res, nil
}

func platformGluenets(c *apis.StorageContext, t time.Time, agent *meta.Agent) ([]interface{}, error) {
	var agentRuntime *meta.AgentRunTime
	if err := c.DataBase.AgentRuntimeGet(agent.Guid, &agentRuntime); err != nil {
		return nil, fmt.Errorf("gluenets agent runtime %v", err)
	}

	if agentRuntime.Platform != apis.PlatfromGluenets {
		return nil, fmt.Errorf("agent is not gluenets")
	}
	var apps = make(map[string][]string)
	for _, v := range strings.Split(agentRuntime.Containers, "|") {
		vs := strings.Split(v, ",")
		if len(vs) == 2 {
			apps[vs[1]] = append(apps[vs[1]], vs[0])
		}
	}

	//if agentRuntime.Cells == "" {
	//	return nil, nil
	//}
	//
	var appsData []interface{}
	//var glueCells glue.Cells
	//if err := json.Unmarshal([]byte(agentRuntime.Cells), &glueCells); err != nil {
	//	return nil, err
	//}
	//
	////fmt.Printf("\n\n %v \n\n", glueCells.Cells)
	//
	//var apps = make(map[string][]string)
	//for _, v := range glueCells.Cells {
	//	apps[v.Guid] = append(apps[v.Guid], v.Name)
	//}

	for k, v := range apps {

		//fmt.Printf("\n\n key:%v \n\n", k)

		var ins *meta.Instance
		if err := c.DataBase.InstanceGet(k, &ins); err != nil {
			if errors.Is(err, gerrors.DataNotFind) {
				continue
			}
			return nil, fmt.Errorf("get agent error %v", err)
		}

		//c.Logger.Warnf("data %v exist", ins[0])

		var z = meta.Zone{
			Name:       ins.Name,
			Type:       "instance",
			Guid:       k,
			UpdateTime: t,
			State:      "alive",
			Info:       ins,
		}

		z.ChildAlive = len(v)
		z.ChildSize = len(v)

		containers, err := glueContainerInfo(c, t, k, ins.InstanceValue, v)
		if err != nil {
			return nil, err
		}
		z.ChildData = containers

		appsData = append(appsData, z)
	}
	return appsData, nil
}

func glueContainerInfo(c *apis.StorageContext, t time.Time, appGuid, intanceValue string, containers []string) ([]interface{}, error) {
	var gin = glue.Instance{}
	if err := json.Unmarshal([]byte(intanceValue), &gin); err != nil {
		return nil, err
	}

	var gContainers = glue.Containers{}
	if err := yaml.Unmarshal([]byte(gin.InstanceYaml), &gContainers); err != nil {
		return nil, err
	}

	var res []interface{}
	for _, v := range containers {
		var z = meta.Zone{
			Name:       v,
			Type:       "container",
			Guid:       appGuid,
			UpdateTime: t,
			State:      "alive",
			Info:       gContainers.Containers[v],
		}

		z.ChildAlive = 0
		z.ChildData = nil
		z.ChildSize = 0

		res = append(res, z)
	}

	return res, nil
}
